package aco;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import aco.Ants.Ant;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

// 应用方法类，在算法中用到的小方法储存在此类中。
public class Utilities {

    private static Random random;

    static int seed;
    static int rowIndex = 1;

    //private static String filePath = "output/Experiments Fuzzy cMeans.xlsx";

    private static String filePath = "../../VRP/Experimente multi-objective VRPTW/Rulari multi-objective VRPTW - MoACO_D-ACS.xlsx";
    //private static String filePath1 = "../../../Jurnale/jurnal Swarm Intelligence (Springer)/Experimente/Fronturi Pareto algoritmi (pe parcurs)/ACO MinMax_vers. noua/ParetoFront_" + TSP_ACO.instanceName + " (m=" + MTsp.m + ")_amplitude_";
    private static String filePath1 = "../../VRP/Experimente multi-objective VRPTW/ParetoFront VRPTW_" + VRPTW.instance.name + " (MoACO_D-ACS)" + ".xlsx";
    private static String filePath2 = "../../VRP/Experimente multi-objective VRPTW/fisiere intermediare/Rulari MoACO_D-ACS_" + VRPTW.instance.name + "_no of vehicles and total distance.txt";
    private static String filePath3 = "../../Doctorat/VRP/VRPTW Solomon instances - data points.xlsx";
    //private static String filePath4 = "../../VRP/Experimente dynamic VRPTW Solomon/Rulari DVRPTW_ACS MinSum/";
    //private static String filePath4 = "output/";
    private static String filePath4 = "output_count_solutions/";
    //private static String filePath5 = "../../VRP/Experimente dynamic VRPTW Solomon/Rulari DVRPTW_ACS MinSum/Rulari DVRPTW_ACS MinSum.xlsx";
    //private static String filePath5 = "output/Rulari DVRPTW_ACS MinSum.xlsx";
    private static String filePath5 = "output_count_solutions/Rulari DVRPTW_ACS MinSum_instante statice_30 rulari.xlsx";

    //for setting the content to be inserted in the Excel file
    private static String tours[];
    private static int nrCities[];
    private static double subtoursCost[];
    private static double totalCost;
    private static double longestSubtour;
    private static double amplitude;

    //for verbose output: save at each 5 iteration the best (minimum) total cost of all m subtours 
    private static ArrayList<Double> iterTotalCost;

    //for verbose output: save at each 5 iteration the cost of the longest tour and the number of the corresponding iteration
    private static ArrayList<Double> iterLongestCost;
    private static ArrayList<Integer> iterNumber;

    //for displaying text with color at console of Linux based systems
    public static final String ANSI_RESET = "\u001B[0m";
    public static final String ANSI_RED = "\u001B[31m";

    // auxiliary routine for sorting an integer array
    // 交换数组 v 和 v2 中的第i个元素和第j个元素的值
    static void swap2(double v[], int v2[], int i, int j) {
        double tmp1;
        int tmp2;

        tmp1 = v[i];
        v[i] = v[j];
        v[j] = tmp1;

        tmp2 = v2[i];
        v2[i] = v2[j];
        v2[j] = tmp2;
    }

    // recursive routine (quicksort) for sorting one array; second array does the same sequence of swaps
    // 根据v[]中元素的值，进行升序排列，同时对 v2[] 中元素的值进行相应更改
    static void sort2(double v[], int v2[], int left, int right) {
        int k, last;
        if (left >= right) return;
        swap2(v, v2, left, (left + right) / 2);
        last = left;
        for (k = left + 1; k <= right; k++)
            if (v[k] < v[left])
                swap2(v, v2, ++last, k);
        swap2(v, v2, left, last);

        sort2(v, v2, left, last);
        sort2(v, v2, last + 1, right);
    }

    //auxiliary routine for sorting an integer array and a double array
    // 交换数组 v[] 和 v2[] 中的第i个元素和第j个元素（注意数组类型）
    static void swap2_(Double v[], Integer v2[], int i, int j) {
        double tmp1;
        int tmp2;

        tmp1 = v[i];
        v[i] = v[j];
        v[j] = tmp1;

        tmp2 = v2[i];
        v2[i] = v2[j];
        v2[j] = tmp2;
    }

    //recursive routine (quicksort) for sorting one array; second array does the same sequence of swaps
    static void sort2_(Double v[], Integer v2[], int left, int right) {
        int k, last;

        if (left >= right)
            return;
        swap2_(v, v2, left, (left + right) / 2);
        last = left;
        for (k = left + 1; k <= right; k++)
            if (v[k] < v[left])
                swap2_(v, v2, ++last, k);
        swap2_(v, v2, left, last);
        sort2_(v, v2, left, last);
        sort2_(v, v2, last + 1, right);
    }

    //generate a random number that is uniformly distributed in [0,1]
    static double random01() {
        if (random == null) {
            random = new Random();
        }
        return random.nextDouble();
    }

    //    先创建一个工作簿，一个工作簿可以有多个工作表，一个工作表可以有多个行，一个行可以有多个单元格
//　　工作簿 ----------->XSSFWorkbook
//　　工作表 ----------->XSSFSheet
//　　行    ----------->XSSFRow
//　　单元格 ----------->XSSFCell
//  	将问题的数据（VRPTW.instance）写入到excel文件中（应该是写入到已知文件中，filePath3）
    static void writeInputDataPoints() {
        //the file already exists
        if (new File(filePath3).canRead()) {
            try {
//	    		0. 准备工作
                FileInputStream file = new FileInputStream(new File(filePath3));

                //Create Workbook instance holding reference to .xlsx file（工作簿）
                XSSFWorkbook workbook1 = new XSSFWorkbook(file);

                //Get first/desired sheet from the workbook（工作表）
                XSSFSheet sheet1 = workbook1.getSheetAt(11);

                //define a cell style for bold font（设置字体样式）
                CellStyle style = workbook1.createCellStyle();
                Font font = workbook1.createFont();
                font.setBoldweight(Font.BOLDWEIGHT_BOLD);
                style.setFont(font);

//	            1. 创建表头（工作簿、工作表、行、单元格，都是从0开始计数的）
                Row r1 = sheet1.getRow(0);
                if (r1 == null) {
                    // First cell in the row, create
                    r1 = sheet1.createRow(0);
                }

                Cell c = r1.getCell(0);
                if (c == null) {
                    c = r1.createCell(0);
                }
                c.setCellValue("VRPTW instance - " + VRPTW.instance.name + "; data point coordinates corresponding to customers' requests");
                c.setCellStyle(style);

                Row r = sheet1.getRow(2);
                if (r == null) {
                    // First cell in the row, create
                    r = sheet1.createRow(2);
                }

                Cell c1 = r.getCell(0);
                if (c1 == null) {
                    c1 = r.createCell(0);
                }
                c1.setCellValue("Point #");
                c1.setCellStyle(style);

                c1 = r.getCell(1);
                if (c1 == null) {
                    c1 = r.createCell(1);
                }
                c1.setCellValue("X Coord");
                c1.setCellStyle(style);

                c1 = r.getCell(2);
                if (c1 == null) {
                    c1 = r.createCell(2);
                }
                c1.setCellValue("Y Coord");
                c1.setCellStyle(style);


//	            2. 写入数据：将城市编号和相应坐标写入到excel中
                int size = VRPTW.instance.nodes.length;
                int rowIndex = 3; // 从第4行开始写城市数据
                double x, y;
                for (int i = 0; i < size; i++) {
                    x = VRPTW.instance.nodes[i].x;
                    y = VRPTW.instance.nodes[i].y;
                    r = sheet1.getRow(rowIndex + i); // 行
                    if (r == null) {
                        // First cell in the row, create
                        //System.out.println("Empty row, create new one");
                        r = sheet1.createRow(rowIndex + i);
                    }

//		            2.1 写入城市编号
                    c1 = r.getCell(0);
                    if (c1 == null) {
                        // New cell
                        //System.out.println("Empty cell, create new one");
                        c1 = r.createCell(0);
                    }
                    c1.setCellValue(i); // 行的第一个单元格写编号（应该是城市的编号）

//					2.2 写入x坐标
                    c1 = r.getCell(1);
                    if (c1 == null) {
                        // New cell
                        //System.out.println("Empty cell, create new one");
                        c1 = r.createCell(1);
                    }
                    c1.setCellValue(x);

//		            2.3 写入y坐标
                    c1 = r.getCell(2);
                    if (c1 == null) {
                        // New cell
                        //System.out.println("Empty cell, create new one");
                        c1 = r.createCell(2);
                    }
                    c1.setCellValue(y);

                }

                //Write the workbook in file system（这一步好像没有意义！）
                FileOutputStream out = new FileOutputStream(new File(filePath3));
                workbook1.write(out);
                out.close();

                //System.out.println("Written successfully on disk.");
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("File not exists..");
        }
    }

    //    写excel文件（这应该是一个重复写入的过程）。将n, m, result写入到excel中
    static void writeExcel(int n, int m, int result) {
        //the file already exists; we should add a new row as the last one in the Excel file
        if (new File(filePath).canRead()) {
            //System.out.println("File already exists..");
            try {
//	    		1. 准备工作
                FileInputStream file = new FileInputStream(new File(filePath));

                //Create Workbook instance holding reference to .xlsx file（工作簿）
                XSSFWorkbook workbook1 = new XSSFWorkbook(file);

                //Get first/desired sheet from the workbook（工作表）
                XSSFSheet sheet1 = workbook1.getSheetAt(2); // 对第三个表进行操作
                int countRows = sheet1.getLastRowNum() + 1; // 接着表已有的部分，往后写
                Row newRow = sheet1.createRow(countRows++); // 这里是先操作，再+1

//				2. 写入信息（x, y, result）
                int cellnum = 0;
                Cell cell = newRow.createCell(cellnum++);
                cell.setCellValue(n);
                cell = newRow.createCell(cellnum++);
                cell.setCellValue(m);
                cell = newRow.createCell(cellnum++);
                cell.setCellValue(result);

                //Write the workbook in file system
                FileOutputStream out = new FileOutputStream(new File(filePath));
                workbook1.write(out);
                out.close();

                //System.out.println("Written successfully on disk.");
            } catch (Exception e) {
                e.printStackTrace();
            }

        } else { // 如果不存在，就创建工作簿
            //Blank workbook（工作簿）
            XSSFWorkbook workbook2 = new XSSFWorkbook();

            //Create a blank sheet（工作表）
            XSSFSheet sheet2 = workbook2.createSheet("Results - 51 cities");

            //Iterate over data and write to sheet（在表中写入信息）
            int rownum = 0, cellnum = 0;
            Row row = sheet2.createRow(rownum++);
            Cell cell = row.createCell(cellnum++);
            cell.setCellValue(n);
            cell = row.createCell(cellnum++);
            cell.setCellValue(m);
            cell = row.createCell(cellnum++);
            cell.setCellValue(result);

            try {
                //Write the workbook in file system
                FileOutputStream out = new FileOutputStream(new File(filePath));
                workbook2.write(out);
                out.close();

                //System.out.println("Written successfully on disk.");
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * 将运算结果写入到excel中
     *
     * @param trialNumber：测试的次数
     * @param saveIterCosts：优化算法不运行的时候，是否要继续写入结果（也就是说，只记录优化中产生的结果）
     */
    static void writeResultsExcel(int trialNumber, boolean saveIterCosts) {
        Row r, r1;
        Cell c;
        int index1 = 0, index2 = 0, index3 = 0, index4 = 0, index5 = 0;
        //int index6 = 0;

        //the file already exists; we should add a new row as the last one in the Excel file
        if (new File(filePath).canRead()) {
            //System.out.println("File already exists..");
            try {

//	    		1. 准备工作：文件输入流
                FileInputStream file = new FileInputStream(new File(filePath));

                //Create Workbook instance holding reference to .xlsx file
                XSSFWorkbook workbook1 = new XSSFWorkbook(file);

                int startIndex = 0, rowIndex = 0;
	            /*switch (VRPTW.m) {
	            	case 2: 
	            		startIndex = 0;
	            		rowIndex = 4;
	            		break;
	            	case 3: 
	            		startIndex = 2;
	            		rowIndex = 5;
	            		break;
	            	case 5: 
	            		startIndex = 4;
	            		rowIndex = 7;
	            		break;
	            	case 7: 
	            		startIndex = 6;
	            		rowIndex = 9;
	            		break;
	            	default:
	            		System.out.println("Unknown value for m");
	            		break;         
	            }*/

	            /*XSSFSheet sheet2 = workbook1.getSheetAt(startIndex + 1);  //for number of assigned cities
	            XSSFSheet sheet3 = workbook1.getSheetAt(startIndex + 2);  //for cost of individual subtours
	            XSSFSheet sheet4 = workbook1.getSheetAt(startIndex + 3);  //for total cost of subtours
	            XSSFSheet sheet5 = workbook1.getSheetAt(startIndex + 4);  //for verbose output of total cost at each 5 iteration
	            */

//				2. 记录
                // Get desired sheet from the workbook
                // for tours
                XSSFSheet sheet1 = workbook1.getSheetAt(startIndex); // 记录：所有的路径
                // for verbose output of longest cost at each 5 iteration
                XSSFSheet sheet2 = workbook1.getSheetAt(startIndex + 1);  // 记录：每五次迭代得到的最大花费

//				3. 定义字体
                //define a cell style for bold font
                CellStyle style = workbook1.createCellStyle();
                Font font = workbook1.createFont();
                font.setBoldweight(Font.BOLDWEIGHT_BOLD);
                style.setFont(font);

                //define style with bold font and blue color for font
                CellStyle styleBoldBlue = workbook1.createCellStyle();
                font = workbook1.createFont();
                font.setBoldweight(Font.BOLDWEIGHT_BOLD);
                font.setColor(IndexedColors.BLUE.index);
                styleBoldBlue.setFont(font);

//	            4. 开始写表格
                index1 = 133; // 从第133行开始计算
                if (!saveIterCosts) { // 如果此时没有使用优化迭代器


                    //write only once the name of the algorithm that was run
//					4.1 写表格标题
                    if (trialNumber == 0) { // 判断是不是表头（或者判断是不是一个新的开始）
                        r = sheet1.getRow(index1);
                        if (r == null) {
                            // First cell in the row, create
                            //System.out.println("Empty row, create new one");
                            r = sheet1.createRow(index1);
                        }

                        c = r.getCell(0);
                        if (c == null) {
                            // New cell
                            //System.out.println("Empty cell, create new one");
                            c = r.createCell(0);
                        }
                        c.setCellValue("Obtained solutions (values) after running new version (ACS MinMax global, voiajor si oras alesi simultan) with local search");
                        c.setCellStyle(styleBoldBlue);
                    }

//                    4.2 写表头，各个栏
                    //write only once the table header
                    index1 = index1 + 3;
                    r = sheet1.getRow(index1);
                    if (r == null) {
                        // First cell in the row, create
                        //System.out.println("Empty row, create new one");
                        r = sheet1.createRow(index1);
                    }

                    c = r.getCell(0);
                    if (c == null) {
                        // New cell
                        //System.out.println("Empty cell, create new one");
                        c = r.createCell(0);
                    }
                    c.setCellValue("Run #");
                    c.setCellStyle(style);

                    c = r.getCell(1);
                    if (c == null) {
                        // New cell
                        //System.out.println("Empty cell, create new one");
                        c = r.createCell(1);
                    }
                    c.setCellValue("MinMax (cost of longest subtour)");
                    c.setCellStyle(style);

                    c = r.getCell(2);
                    if (c == null) {
                        // New cell
                        //System.out.println("Empty cell, create new one");
                        c = r.createCell(2);
                    }
                    c.setCellValue("Total Cost");
                    c.setCellStyle(style);

                    c = r.getCell(3);
                    if (c == null) {
                        // New cell
                        //System.out.println("Empty cell, create new one");
                        c = r.createCell(3);
                    }
                    c.setCellValue("Amplitude");
                    c.setCellStyle(style);

//                    4.3 写入信息（测试次数、最长路径的最小花费、总花费、振幅）
                    //write number of run
                    index1 = 137 + trialNumber;
                    r = sheet1.getRow(index1);
                    if (r == null) {
                        // First cell in the row, create
                        //System.out.println("Empty row, create new one");
                        r = sheet1.createRow(index1);
                    }

                    c = r.getCell(0);
                    if (c == null) {
                        // New cell
                        //System.out.println("Empty cell, create new one");
                        c = r.createCell(0);
                    }
                    c.setCellValue(trialNumber + 1);

                    //write MinMax (cost of longest subtour)
                    double longestSubtour = getLongestSubtour();
                    c = r.getCell(1);
                    if (c == null) {
                        // New cell
                        //System.out.println("Empty cell, create new one");
                        c = r.createCell(1);
                    }
                    c.setCellValue(longestSubtour);

                    //write total cost
                    double totalCost = getTotalCost();
                    c = r.getCell(2);
                    if (c == null) {
                        // New cell
                        //System.out.println("Empty cell, create new one");
                        c = r.createCell(2);
                    }
                    c.setCellValue(totalCost);

                    //write amplitude
                    double amplitude = getAmplitude();
                    c = r.getCell(3);
                    if (c == null) {
                        // New cell
                        //System.out.println("Empty cell, create new one");
                        c = r.createCell(3);
                    }
                    c.setCellValue(amplitude);
                }

//                继续写表格，这里写的是？？？
                index5 = 859; // 为什么行数不一样？？？
                if (saveIterCosts) { // 如果此时正在使用优化迭代器

                    //write only once the name of the algorithm that was run
                    if (trialNumber == 0) {

//                        1. 写表格标题
                        r = sheet2.getRow(index5);
                        if (r == null) {
                            // First cell in the row, create
                            //System.out.println("Empty row, create new one");
                            r = sheet2.createRow(index5);
                        }

                        c = r.getCell(0);
                        if (c == null) {
                            // New cell
                            //System.out.println("Empty cell, create new one");
                            c = r.createCell(0);
                        }
                        c.setCellValue("Longest cost of subtour at each 5 iteration after running new version (ACS MinMax global, voiajor si oras alesi simultan) with local search");
                        c.setCellStyle(styleBoldBlue);

//                        2. 写迭代次数的表头：Nr Iter
                        int tempIndex = index5 + 3;
                        r = sheet2.getRow(tempIndex);
                        if (r == null) {
                            // First cell in the row, create
                            //System.out.println("Empty row, create new one");
                            r = sheet2.createRow(tempIndex);
                        }
                        ArrayList<Integer> iterNumber = getIterNumber(); // 迭代次数

                        c = r.getCell(0);
                        if (c == null) {
                            // New cell
                            //System.out.println("Empty cell, create new one");
                            c = r.createCell(0);
                        }
                        c.setCellValue("Nr Iter");
                        c.setCellStyle(style);

//                        3. 把每一行的第一列都写出来（第一列为迭代次数）
                        int indexTemp = 0;
                        for (int j = 0; j < iterNumber.size(); j++) {
                            indexTemp = tempIndex + 1 + j;
                            r1 = sheet2.getRow(indexTemp);
                            if (r1 == null) {
                                // First cell in the row, create
                                //System.out.println("Empty row, create new one");
                                r1 = sheet2.createRow(indexTemp);
                            }

                            c = r1.getCell(0);
                            if (c == null) {
                                // New cell
                                //System.out.println("Empty cell, create new one");
                                c = r1.createCell(0);
                            }
                            c.setCellValue(iterNumber.get(j));
                        }
                    }

//                    把数据写入到excel中
                    index5 = index5 + 3; // 与表头，中间间隔三行
                    r = sheet2.getRow(index5);
                    if (r == null) {
                        // First cell in the row, create
                        //System.out.println("Empty row, create new one");
                        r = sheet2.createRow(index5);
                    }

                    //for each trial run save at each 5 iteration the best longest cost of a subtour so far
                    ArrayList<Double> iterLongestCost = getIterLongestCost();
                    int index;

                    //for each run write the table header cell
                    c = r.getCell(trialNumber + 1);
                    if (c == null) {
                        // New cell
                        //System.out.println("Empty cell, create new one");
                        c = r.createCell(trialNumber + 1);
                    }
                    c.setCellValue("Run " + (trialNumber + 1));
                    c.setCellStyle(style);

                    for (int j = 0; j < iterLongestCost.size(); j++) {
                        index = index5 + 1 + j;
                        r1 = sheet2.getRow(index);
                        if (r1 == null) {
                            // First cell in the row, create
                            //System.out.println("Empty row, create new one");
                            r1 = sheet2.createRow(index);
                        }

                        c = r1.getCell(trialNumber + 1);
                        if (c == null) {
                            // New cell
                            //System.out.println("Empty cell, create new one");
                            c = r1.createCell(trialNumber + 1);
                        }
                        c.setCellValue(iterLongestCost.get(j));
                    }
                }

                //Write the workbook in file system
                FileOutputStream out = new FileOutputStream(new File(filePath));
                workbook1.write(out); // 把工作表写进文件系统中
                out.close();

                int nrOfRun = trialNumber + 1;
                System.out.println("\nRun #" + nrOfRun + " written successfully on disk.\n");
            } catch (Exception e) {
                e.printStackTrace();
            }

        } else {
            //Blank workbook
            System.out.println("File " + filePath + " doesn't exists..");

        }
    }

    /**
     * 写入到excel文件中: 编号(应该是解的编号), 优化目标2, 优化目标1, 每条路径的花费
     *
     * @param bestSoFarPareto: 这是个蚂蚁泛型的链表,里面存储的是蚂蚁.当前最优解情况下的
     * @param trial:           测试次数,测试一次创建一个新的工作表,测试结果写入到工作表中
     */
    static void writeParetoSet(ArrayList<Ant> bestSoFarPareto, int trial) {
        Row r;
        Cell c;
        int lineNumber = 0; // 从第一行开始

        //filePath1 += InOut.max_iterations + " iter (ACO MinMax_vers noua).xlsx";
        //System.out.println("file path=" + filePath1);

        //the file already exists; we should add a new row as the last one in the Excel file
        if (new File(filePath1).canRead()) {
            //System.out.println("File already exists..");
            try {
//                0. 准备工作.文件输入流,初始化工作簿,表,行,单元格
                FileInputStream file = new FileInputStream(new File(filePath1));

                //Create Workbook instance holding reference to .xlsx file(工作簿)
                XSSFWorkbook workbook1 = new XSSFWorkbook(file);

                //Get first/desired sheet from the workbook(工作表,一次测试做一个工作表)
                XSSFSheet sheet1 = workbook1.getSheetAt(trial);

                //write table header cells(行)
                r = sheet1.getRow(lineNumber);
                if (r == null) {
                    // First cell in the row, create
                    r = sheet1.createRow(lineNumber);
                }

//                1. 创建表头
                c = r.getCell(0); // (单元格)
                if (c == null) {
                    // New cell
                    c = r.createCell(0);
                }
                c.setCellValue("Point #");

                c = r.getCell(1);
                if (c == null) {
                    // New cell
                    c = r.createCell(1);
                }
                c.setCellValue("Total tours length");

                c = r.getCell(2);
                if (c == null) {
                    // New cell
                    c = r.createCell(2);
                }
                c.setCellValue("Amplitude of tours");

                c = r.getCell(3);
                if (c == null) {
                    // New cell
                    c = r.createCell(3);
                }
                c.setCellValue("List with cost of subtours");

//                2. 循环写入数据(一行一行的写)
                lineNumber++; // 写入表格的第二行(开始写数据了)
                for (int i = 0; i < bestSoFarPareto.size(); i++) {
                    r = sheet1.getRow(i + lineNumber);
                    if (r == null) {
                        // First cell in the row, create
                        r = sheet1.createRow(i + lineNumber);
                    }
//                    2.1 write point id(应该是写入蚂蚁的id,或者是编号)
                    c = r.getCell(0);
                    if (c == null) {
                        // New cell
                        c = r.createCell(0, Cell.CELL_TYPE_NUMERIC);
                    }
                    c.setCellValue(i + 1);
//                    2.2 write total cost and amplitude(在两个单元格分别写入两个优化目标)
                    for (int indexObj = 0; indexObj < 2; indexObj++) {
                        c = r.getCell(indexObj + 1);
                        if (c == null) {
                            // New cell
                            c = r.createCell(indexObj + 1, Cell.CELL_TYPE_NUMERIC);
                        }
                        c.setCellValue(bestSoFarPareto.get(i).costObjectives[indexObj]);
                    }
//                    2.3 write cost of each individual subtour
                    for (int j = 0; j < bestSoFarPareto.get(i).tour_lengths.size(); j++) {
                        c = r.getCell(j + 3);
                        if (c == null) {
                            // New cell
                            c = r.createCell(j + 3);
                        }
                        c.setCellValue(bestSoFarPareto.get(i).tour_lengths.get(j));
                    }
                }

//                3. Write the workbook in file system
                FileOutputStream out = new FileOutputStream(new File(filePath1));
                workbook1.write(out);
                out.close();

                //System.out.println("\nWritten Pareto front points successfully on disk.\n");
                int nrOfRun = trial + 1;
                System.out.println("\nRun #" + nrOfRun + " written Pareto front points successfully on disk.\n");
            } catch (Exception e) {
                e.printStackTrace();
            }

        } else {
            System.out.println(" File " + filePath1 + " doesn't exists");
        }

    }


    /**
     * 把两个优化目标和每条路径的花费,作为一行,写入到txt文件中.
     *
     * @param bestSoFarPareto: 当前最优蚁群的集合
     */
//    save in a .txt output file the best solution resulted after a run to be later used when computing the Pareto front
    static void writeParetoSolutions(ArrayList<Ant> bestSoFarPareto) {
        File f = new File(filePath2);
        double[] objValues = new double[2]; // 存储优化目标结果的数组

        try {
//            创建写入txt的缓冲字符流
            BufferedWriter bw = new BufferedWriter(new FileWriter(f, true));

            for (int i = 0; i < bestSoFarPareto.size(); i++) { // 遍历解中的每个蚂蚁
                if (rowIndex > 0 && i != 0) {
                    bw.newLine();
                }
                bw.write(rowIndex + "\t"); // "\t"相当于格式化的空格,使得看起来整齐
                //get values total cost and amplitude
                for (int indexObj = 0; indexObj < 2; indexObj++) {
                    objValues[indexObj] = bestSoFarPareto.get(i).costObjectives[indexObj];
                }
                bw.write(objValues[0] + "\t");
                //write cost of each individual subtour
                for (int j = 0; j < bestSoFarPareto.get(i).tour_lengths.size(); j++) {
                    bw.write(bestSoFarPareto.get(i).tour_lengths.get(j) + "\t");
                }
                bw.write(objValues[1] + "\t");

                rowIndex++;
            }
            bw.newLine();
            bw.close();
        } catch (IOException e) {
            System.out.println("error writing file");
        }
    }

    /**
     * @param trial:测试次数
     * @param fileName:txt文件的名称
     * @param scalledValue:什么意思???
     * @param isValid:得到的优化结果是否合法
     */
    static void writeFinalSolution(int trial, String fileName, double scalledValue, boolean isValid) {
        String name = filePath4 + fileName;
        File f = new File(name);

        try {
            BufferedWriter bw = new BufferedWriter(new FileWriter(f, true)); // 字符流,写入到txt文件中

            if (trial > 0) {
                bw.newLine();
            }
            bw.write("Run Ant Colony System #" + (trial + 1));

            bw.write("\nFinal best solution >> No. of used vehicles=" + Ants.best_so_far_ant.usedVehicles + " total tours length=" + Ants.best_so_far_ant.total_tour_length + " (scalled value = " + scalledValue + ")");

//            下面将城市编号序列(也就是每条路线)写入到txt文件中
            bw.newLine();
            for (int i = 0; i < Ants.best_so_far_ant.usedVehicles; i++) {
                int tourLength = Ants.best_so_far_ant.tours.get(i).size();
                for (int j = 0; j < tourLength; j++) {
                    int city = Ants.best_so_far_ant.tours.get(i).get(j);
                    city = city + 1;  //so as to correspond to the city indexes from the VRPTW input file
                    bw.write(city + " ");
                }
                bw.newLine();
            }
            bw.write("\nTotal number of evaluations: " + InOut.noEvaluations); // 写入求解次数???
            bw.write("\nAdded nodes=" + Controller.addedNodes); // 锁定的结点的数量,或者新出现结点的数量
            if (isValid) { // 判断是否合法
                bw.write("\nThe final solution is valid (feasible)..");
            } else {
                bw.write("\nThe final solution is not valid (feasible)..");
            }

            bw.newLine();
            bw.close();

        } catch (IOException e) {
            System.out.println("error writing file");
            e.printStackTrace();
        }
    }

    /**
     * 将最终解写入到excel文件中
     *
     * @param trial:测试次数
     * @param scalledValue
     */
    static void writeExcelFinalSolution(int trial, double scalledValue) {
        Row r;
        Cell c;
        int index1 = 0;

        //the file already exists; we should add a new row as the last one in the Excel file
        if (new File(filePath5).canRead()) {
            //System.out.println("File already exists..");
            try {
                FileInputStream file = new FileInputStream(new File(filePath5)); // 输入流

                //Create Workbook instance holding reference to .xlsx file(工作簿)
                XSSFWorkbook workbook1 = new XSSFWorkbook(file);

                //Get desired sheet from the workbook(工作表)
                XSSFSheet sheet1 = workbook1.getSheetAt(0);

                //define a cell style for bold font(设置字体)
                CellStyle style = workbook1.createCellStyle();
                Font font = workbook1.createFont();
                font.setBoldweight(Font.BOLDWEIGHT_BOLD);
                style.setFont(font);

                //define style with bold font and blue color for font(单元格格式)
                CellStyle styleBoldBlue = workbook1.createCellStyle();
                font = workbook1.createFont();
                font.setBoldweight(Font.BOLDWEIGHT_BOLD);
                font.setColor(IndexedColors.BLUE.index);
                styleBoldBlue.setFont(font);

                index1 = 8;   //8  //26

                index1 = index1 + trial;
                r = sheet1.getRow(index1); // 行
                if (r == null) {
                    // First cell in the row, create
                    //System.out.println("Empty row, create new one");
                    r = sheet1.createRow(index1);
                }

                int nrOfRun = trial + 1;
                //write trial number (Run #)
                c = r.getCell(15);
                if (c == null) {
                    // New cell
                    //System.out.println("Empty cell, create new one");
                    c = r.createCell(15);
                }
                c.setCellValue(nrOfRun);

                //write number of used vehicles
                c = r.getCell(16);
                if (c == null) {
                    // New cell
                    //System.out.println("Empty cell, create new one");
                    c = r.createCell(16);
                }
                c.setCellValue(Ants.best_so_far_ant.usedVehicles);

                //write total traveled distance
                c = r.getCell(17);
                if (c == null) {
                    // New cell
                    //System.out.println("Empty cell, create new one");
                    c = r.createCell(17);
                }
                c.setCellValue(scalledValue);

                //write the total number of feasible solutions
                c = r.getCell(18);
                if (c == null) {
                    // New cell
                    //System.out.println("Empty cell, create new one");
                    c = r.createCell(18);
                }
                c.setCellValue(InOut.noSolutions);

                //Write the workbook in file system
                FileOutputStream out = new FileOutputStream(new File(filePath5));
                workbook1.write(out);
                out.close();

                System.out.println("\nRun #" + nrOfRun + " written successfully on disk.\n");
            } catch (Exception e) {
                e.printStackTrace();
            }

        } else {
            //Blank workbook
            System.out.println("File " + filePath5 + " doesn't exists..");
        }
    }

    public static String[] getTours() {
        return tours;
    }

    public static void setTours(String[] tours) {
        Utilities.tours = tours;
    }

    public static int[] getNrCities() {
        return nrCities;
    }

    public static void setNrCities(int[] nrCities) {
        Utilities.nrCities = nrCities;
    }

    public static double[] getSubtoursCost() {
        return subtoursCost;
    }

    public static void setSubtoursCost(double[] subtoursCost) {
        Utilities.subtoursCost = subtoursCost;
    }

    public static double getTotalCost() {
        return totalCost;
    }

    public static void setTotalCost(double totalCost) {
        Utilities.totalCost = totalCost;
    }

    public static ArrayList<Double> getIterTotalCost() {
        return iterTotalCost;
    }

    public static void setIterTotalCost(ArrayList<Double> iterTotalCost) {
        Utilities.iterTotalCost = iterTotalCost;
    }

    public static ArrayList<Double> getIterLongestCost() {
        return iterLongestCost;
    }

    public static ArrayList<Integer> getIterNumber() {
        return iterNumber;
    }

    public static void setIterLongestCost(ArrayList<Double> iterLongestCost) {
        Utilities.iterLongestCost = iterLongestCost;
    }

    public static void setIterNumber(ArrayList<Integer> iterNumber) {
        Utilities.iterNumber = iterNumber;
    }

    public static double getLongestSubtour() {
        return longestSubtour;
    }

    public static void setLongestSubtour(double longestSubtour) {
        Utilities.longestSubtour = longestSubtour;
    }

    public static double getAmplitude() {
        return amplitude;
    }

    public static void setAmplitude(double amplitude) {
        Utilities.amplitude = amplitude;
    }

    //    把解从filename.txt文件中读取出来,返回出solution
    public static ArrayList<ArrayList<Integer>> readSolution(String fileName) {
        File file = new File("input/" + fileName);
        BufferedReader in = null;
        int value;

        ArrayList<ArrayList<Integer>> solution = new ArrayList(); // 问题的解
        ArrayList<Integer> tour; // 路径

        if (file.exists()) {
            try {
//                0. 先读取第一行
                in = new BufferedReader(new FileReader(file));
                String line = in.readLine();
                while (line != null) {
//                    1. 将数据写入每条路径tour中
                    tour = new ArrayList<Integer>();
                    String[] strRecord = line.trim().split(" "); // 将txt中的每一行,读取,转化为数组
                    for (int i = 0; i < strRecord.length; i++) {
                        try {
                            value = Integer.parseInt(strRecord[i]) - 1;
                            tour.add(value);
                        } catch (NumberFormatException e) {
                            System.out.println("NumberFormatException " + e.getMessage() + " record=" + strRecord[i] + " line=" + line);
                        }
                    }
//                    2. 将每条路径tour写入到总的解中solution
                    solution.add(tour);
//                    3. 读取下一行
                    line = in.readLine();
                }
                in.close();
            } catch (FileNotFoundException ignored) {
            } catch (IOException e) {
                System.out.println("Error occurred while reading file: " + file + " " + e.getMessage());
            }
        }
        return solution;
    }

    //    检查可行性(检查时间窗约束和路径载量约束)
//    printNoNodes不知道什么意思???
    public static boolean checkFeasibility(Ant a, VRPTW vrp, boolean printNoNodes) {
        boolean isFeasible = true;
        int currentCity, prevCity, addedNodes = 0;
        double currentQuantity, currentTime;
        double distance, arrivalTime, beginService;
        ArrayList<Request> reqList = vrp.getRequests();

        for (int indexTour = 0; indexTour < a.usedVehicles; indexTour++) {
            currentQuantity = reqList.get(0).getDemand();
            currentTime = 0.0;
            for (int currentPos = 1; currentPos < a.tours.get(indexTour).size(); currentPos++) {
                if (currentPos < a.tours.get(indexTour).size() - 1) {
                    addedNodes++;
                }
				/*if (addedNodes == 0) {
					System.out.println("Possible empty tour: " + a.tours.get(indexTour).size());
				}*/
                prevCity = a.tours.get(indexTour).get(currentPos - 1);
                currentCity = a.tours.get(indexTour).get(currentPos);
                currentQuantity += reqList.get(currentCity + 1).getDemand();

                distance = VRPTW.instance.distance[prevCity + 1][currentCity + 1];
                arrivalTime = currentTime + reqList.get(prevCity + 1).getServiceTime() + distance;
                beginService = Math.max(arrivalTime, reqList.get(currentCity + 1).getStartWindow());
//                1. 检查时间窗约束
                if (beginService > reqList.get(currentCity + 1).getEndWindow()) {
                    //isFeasible = false;
                    System.out.println("Time window constraint violated");
                    return false;
                }
                currentTime = beginService;

            }
//            2. 检查路径载量约束
            if (currentQuantity > vrp.getCapacity()) {
                //isFeasible = false;
                System.out.println("Capacity constraint violated");
                return false;
            }
        }
        if (printNoNodes) {
            System.out.println("Added nodes=" + addedNodes);
        }
//        添加数量
        Controller.addedNodes = addedNodes;
        return isFeasible;

    }

//  这是测试的线程，一般不会被调用
    public static void main(String[] args) {

//        1. 初始化输入流(算例)和输出文件
        //read the data from the input file(从算例文件中读取数据)
        DataReader reader = new DataReader(Controller.vrpInstance);
        //read the data from the file
        VRPTW vrpInstance = reader.read();
        int trial = 1; // 第一次测试trial
        String solutionFile = "RC203_solutie_new.txt"; // 最终得到的解文件

        System.out.println("\nVRPTW_ACS MinSum_alegere simultana >> Solving VRPTW instance: " + VRPTW.instance.name);
        System.out.println("No. of customers' requests (except the depot): " + VRPTW.n);
        //System.out.println("No. of desired tours (used vehicles): " + VRPTW.m);

//        2. 初始化优化程序
        InOut.init_program(args, trial, vrpInstance, 0.0);

//        我感觉中间应该是切换线程了,这中间应该是一直在进行优化运算
//**************************************************************************************************

//        3. 从解文件中,读取出最优的解.并判断解是否可行.
        Ants.ants[0].tours = readSolution(solutionFile);
        boolean res = checkFeasibility(Ants.ants[0], vrpInstance, true);
        if (res) {
            System.out.println("Solution is feasible");
        } else {
            System.out.println("Solution is not feasible");
        }
    }
}